package com.study.sbsummary.config.threadpool;

import com.study.sbsummary.config.apollo.ApolloConfigBean;
import lombok.Getter;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.*;

/**
 * @Description
 * @Author hushichao
 * @Date 19:23 2022-02-25
 **/
@Component
public class ThreadPoolConfig implements InitializingBean, DisposableBean {

    @Getter
    private ExecutorService executorService;

    @Autowired
    private ApolloConfigBean apolloConfigBean;

    @Override
    public void afterPropertiesSet() throws Exception {
        executorService = new ThuelyThreadPoolExecutor(apolloConfigBean.getCorePoolSize(), apolloConfigBean.getMaximumPoolSize(),
                apolloConfigBean.getKeepAliveTime(), TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(apolloConfigBean.getWorkQueue()),
                new NamedThreadFactory("friendsDoc", false));
    }

    @Override
    public void destroy() throws Exception {
        executorService.shutdown();
    }

    public static class ThuelyThreadPoolExecutor extends ThreadPoolExecutor {

        public ThuelyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
        }

        public ThuelyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
        }

        public ThuelyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
        }

        public ThuelyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
        }

        @Override
        public <T> Future<T> submit(Callable<T> task) {
            TruelyCallable truelyCallable = new TruelyCallable(task, RequestContext.isOnlineTest());
            return super.submit(truelyCallable);
        }

        @Override
        public <T> Future<T> submit(Runnable task, T result) {
            TruelyRunnable truelyRunnable = new TruelyRunnable(task, RequestContext.isOnlineTest());
            return super.submit(truelyRunnable, result);
        }

        @Override
        public Future<?> submit(Runnable task) {
            TruelyRunnable truelyRunnable = new TruelyRunnable(task, RequestContext.isOnlineTest());
            return super.submit(truelyRunnable);
        }

        @Override
        public void execute(Runnable command) {
            TruelyRunnable truelyRunnable = new TruelyRunnable(command, RequestContext.isOnlineTest());
            super.execute(truelyRunnable);
        }
    }

    public static class TruelyRunnable implements Runnable {
        private Runnable runnable;
        private boolean onlineTest;

        public TruelyRunnable(Runnable runnable) {
            this(runnable, false);
        }

        public TruelyRunnable(Runnable runnable, boolean onlineTest) {
            this.runnable = runnable;
            this.onlineTest = onlineTest;
        }

        public boolean isOnlineTest() {
            return this.onlineTest;
        }

        @Override
        public void run() {
            try {
                RequestContext.onlineTest(onlineTest);
                runnable.run();
            } finally {
                RequestContext.clear();
            }
        }
    }

    public static class TruelyCallable implements Callable {
        private Callable callable;
        private boolean onlineTest;

        public TruelyCallable(Callable callable) {
            this(callable, false);
        }

        public TruelyCallable(Callable callable, boolean onlineTest) {
            this.callable = callable;
            this.onlineTest = onlineTest;
        }

        public boolean isOnlineTest() {
            return this.onlineTest;
        }

        @Override
        public Object call() throws Exception {
            try {
                RequestContext.onlineTest(onlineTest);
                return callable.call();
            } finally {
                RequestContext.clear();
            }
        }
    }
}
